package com.liustar.common.util;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

/**
 * md5,sha,des,3des,aes,base64
 */
public class CryptoUtil {
    private static final byte[] KEY_DES = {31, -113, -65, 52, -92, -22, -94, -110};
    private static final byte[] KEY_3DES = {-56, 74, 117, 21, 22, 1, 69, 82, 122, -5, -122, -53, 118, 61, -22, -63, -36, 76, 32, -48, -20, -5, 19, 61};
    private static final byte[] KEY_AES = {62, -50, 113, -69, 36, 9, -47, 28, -33, 123, 126, 108, 93, -106, 14, -81};
    private static final byte[] KEY_HMAC = {-56, 74, 117, 21, 22, 1, 69, 82, 122, -5, 62, -50, 113, -69, 36, 9, -47, 28, -33, 123, 126, 108, 93, -106, 14, -81, -122, -53, 118, 61, -22, -63, -36, 76,
            32, -48, -20, -5, 19, 61};

    /**
     * MD5加密
     *
     * @param origin
     * @return
     */
    public static String md5(String origin) {
        return md5(origin, "utf-8");
    }

    /**
     * MD5加密
     *
     * @param origin
     * @param charsetName
     * @return
     */
    public static String md5(String origin, String charsetName) {
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] bytes = md5.digest(origin.getBytes(charsetName));
            StringBuilder ret = new StringBuilder(bytes.length << 1);
            for (int i = 0; i < bytes.length; i++) {
                ret.append(Character.forDigit((bytes[i] >> 4) & 0xf, 16));
                ret.append(Character.forDigit(bytes[i] & 0xf, 16));
            }
            return ret.toString();
        } catch (Exception e) {
            return null;
        }
    }

    public static String sha(String origin) {
        return sha(origin, "utf-8");
    }

    /**
     * SHA加密
     *
     * @param str
     * @return
     */
    public static String sha(String str, String charsetName) {
        try {
            MessageDigest sha = MessageDigest.getInstance("SHA");
            byte[] bytes = sha.digest(str.getBytes(charsetName));
            StringBuilder ret = new StringBuilder(bytes.length << 1);
            for (int i = 0; i < bytes.length; i++) {
                ret.append(Character.forDigit((bytes[i] >> 4) & 0xf, 16));
                ret.append(Character.forDigit(bytes[i] & 0xf, 16));
            }
            return ret.toString();
        } catch (Exception e) {
            return null;
        }
    }

    private static byte[] encodeHMAC(byte[] data) {
        SecretKey secretKey;
        byte[] bytes = null;
        try {
            secretKey = new SecretKeySpec(KEY_HMAC, "HmacMD5");
            Mac mac = Mac.getInstance(secretKey.getAlgorithm());

            mac.init(secretKey);
            bytes = mac.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bytes;
    }

    private static byte[] encode(byte[] data, byte[] key, String type) {
        SecretKey secretKey = new SecretKeySpec(key, type);//恢复密钥
        Cipher cipher = null;//Cipher完成加密或解密工作类
        try {
            cipher = Cipher.getInstance(type);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);//对Cipher初始化，加密模式
            byte[] cipherByte = cipher.doFinal(data);//加密data
            return cipherByte;
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static byte[] decode(byte[] data, byte[] key, String type) {
        SecretKey secretKey = new SecretKeySpec(key, type);//恢复密钥
        Cipher cipher = null;//Cipher完成加密或解密工作类
        try {
            cipher = Cipher.getInstance(type);
            cipher.init(Cipher.DECRYPT_MODE, secretKey);//对Cipher初始化，解密模式
            byte[] cipherByte = cipher.doFinal(data);//解密data
            return cipherByte;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static byte[] encodeDES(byte[] data) {
        return encode(data, KEY_DES, "DES");
    }

    private static byte[] decodeDES(byte[] data) {
        return decode(data, KEY_DES, "DES");
    }

    private static byte[] encode3DES(byte[] data) {
        return encode(data, KEY_3DES, "DESede");
    }

    private static byte[] decode3DES(byte[] data) {
        return decode(data, KEY_3DES, "DESede");
    }

    private static byte[] encodeAES(byte[] data) {
        return encode(data, KEY_AES, "AES");
    }

    private static byte[] decodeAES(byte[] data) {
        return decode(data, KEY_AES, "AES");
    }

    private static byte[] encodeBase64(byte[] data) {
        return Base64.getEncoder().encode(data);
    }

    public static String encodeBase64(String data) {
        return Base64.getEncoder().encodeToString(data.getBytes());
    }

    private static byte[] decodeBase64(byte[] data) {
        return Base64.getDecoder().decode(data);
    }

    public static String decodeBase64(String data) {
        return new String(Base64.getDecoder().decode(data));
    }

    /**
     * DES 对称加密
     *
     * @param data
     * @return
     */
    public static String encryptDES(String data) {
        return null != data ? new String(encodeBase64(encodeDES(data.getBytes()))) : null;
    }

    /**
     * DES 对称解密
     *
     * @param data
     * @return
     */
    public static String decryptDES(String data) {
        return null != data ? new String(decodeDES(decodeBase64(data.getBytes()))) : null;
    }

    /**
     * 3DES 对称加密
     *
     * @param data
     * @return
     */
    public static String encrypt3DES(String data) {
        return null != data ? new String(encodeBase64(encode3DES(data.getBytes()))) : null;
    }

    /**
     * 3DES 对称解密
     *
     * @param data
     * @return
     */
    public static String decrypt3DES(String data) {
        return null != data ? new String(decode3DES(decodeBase64(data.getBytes()))) : null;
    }

    /**
     * AES 对称加密
     *
     * @param data
     * @return
     */
    public static String encryptAES(String data) {
        return null != data ? new String(encodeBase64(encodeAES(data.getBytes()))) : null;
    }

    /**
     * AES 对称解密
     *
     * @param data
     * @return
     */
    public static String decryptAES(String data) {
        return null != data ? new String(decodeAES(decodeBase64(data.getBytes()))) : null;
    }

}
